#include <ani.h>
#include <array>
#include <iostream>
#include <string>
#include <memory>
#include <vector>

#include "base/memory/referenced.h"
#include "core/animation/animator.h"

namespace OHOS::Ace::Ani{

struct AnimatorOption {
    int32_t duration = 0;
    int32_t delay = 0;
    int32_t iterations = 0;
    double begin = 0.0;
    double end = 0.0;
    std::string easing = "ease";
    std::string fill = "none";
    std::string direction = "normal";

    std::string ToString() const
    {
        return "AnimatorOption:[" + std::to_string(duration) + "," + std::to_string(delay) + "," +
               std::to_string(iterations) + "," + std::to_string(begin) + "," + std::to_string(end) + "," + easing +
               "," + fill + "," + direction + "]";
    }
};

static AnimationDirection StringToAnimationDirection(const std::string& direction)
{
    if (direction.compare("alternate") == 0) {
        return AnimationDirection::ALTERNATE;
    } else if (direction.compare("reverse") == 0) {
        return AnimationDirection::REVERSE;
    } else if (direction.compare("alternate-reverse") == 0) {
        return AnimationDirection::ALTERNATE_REVERSE;
    } else {
        return AnimationDirection::NORMAL;
    }
}

static FillMode StringToFillMode(const std::string& fillMode)
{
    if (fillMode.compare("forwards") == 0) {
        return FillMode::FORWARDS;
    } else if (fillMode.compare("backwards") == 0) {
        return FillMode::BACKWARDS;
    } else if (fillMode.compare("both") == 0) {
        return FillMode::BOTH;
    } else {
        return FillMode::NONE;
    }
}

class AnimatorResult final {
public:
    AnimatorResult() = default;
    AnimatorResult(RefPtr<Animator>& animator, std::shared_ptr<AnimatorOption>& option)
        : animator_(animator), option_(option)
    {
        ApplyOption();
    }
    ~AnimatorResult() = default;

    RefPtr<Animator> GetAnimator() const
    {
        return animator_;
    }

    std::shared_ptr<AnimatorOption> GetAnimatorOption() const
    {
        return option_;
    }

    ani_ref GetOnframeRef() const
    {
        return onframe_;
    }

    void SetOnframeRef(const ani_ref& onframe)
    {
        onframe_ = onframe;
    }

    ani_ref GetOnfinishRef() const
    {
        return onfinish_;
    }

    void SetOnfinishRef(const ani_ref& onfinish)
    {
        onfinish_ = onfinish;
    }

    ani_ref GetOncancelRef() const
    {
        return oncancel_;
    }

    void SetOncancelRef(const ani_ref& oncancel)
    {
        oncancel_ = oncancel;
    }

    ani_ref GetOnrepeatRef() const
    {
        return onrepeat_;
    }

    void SetOnrepeatRef(const ani_ref& onrepeat)
    {
        onrepeat_ = onrepeat;
    }

    const RefPtr<Motion>& GetMotion() const
    {
        return motion_;
    }

    void SetMotion(const RefPtr<Motion>& motion)
    {
        motion_ = motion;
    }

    void ApplyOption()
    {
        CHECK_NULL_VOID(animator_);
        CHECK_NULL_VOID(option_);
        if (motion_) {
            // duration not works. Iteration can only be 1. Direction can only be normal.
            animator_->SetIteration(1);
            animator_->SetAnimationDirection(AnimationDirection::NORMAL);
        } else {
            animator_->SetDuration(option_->duration);
            animator_->SetIteration(option_->iterations);
            animator_->SetAnimationDirection(StringToAnimationDirection(option_->direction));
        }
        animator_->SetStartDelay(option_->delay);
        // FillMode not works for motion in animator implementation.
        animator_->SetFillMode(StringToFillMode(option_->fill));
    }

    void Destroy(ani_env env)
    {

    }

private:
    RefPtr<Animator> animator_;
    RefPtr<Motion> motion_;
    std::shared_ptr<AnimatorOption> option_;
    ani_ref onframe_ = nullptr;
    ani_ref onfinish_ = nullptr;
    ani_ref oncancel_ = nullptr;
    ani_ref onrepeat_ = nullptr;
};

std::string ANIUtils_ANIStringToStdString(ani_env *env, ani_string ani_str){
    ani_size  strSize;
    std::cout << "ANIUtils_ANIStringToStdString 00" << std::endl;
    env->String_GetUTF8Size(ani_str, &strSize);
   
    std::vector<char> buffer(strSize + 1); // +1 for null terminator
    char* utf8_buffer = buffer.data();

    //String_GetUTF8 Supportted by https://gitee.com/openharmony/arkcompiler_runtime_core/pulls/3416
    ani_size bytes_written = 0;
    std::cout << "ANIUtils_ANIStringToStdString 11" << std::endl;
    env->String_GetUTF8(ani_str, utf8_buffer, strSize + 1, &bytes_written);
    utf8_buffer[bytes_written] = '\0'; 
    std::string content = std::string(utf8_buffer); 
    std::cout << "ANIUtils_ANIStringToStdString 33" << std::endl;
    return content;
}

// static void ParseExpectedFrameRateRange(ani_env *env, ani_object obj, FrameRateRange& frameRateRange)
// {
//     ani_int minFPS = 0;
//     ani_int maxFPS = 0;
//     ani_int expectedFPS = 0;

//     env->Object_GetPropertyByName_Int(obj, "min", &minFPS);
//     env->Object_GetPropertyByName_Int(obj, "max", &maxFPS);
//     env->Object_GetPropertyByName_Int(obj, "expected", &expectedFPS);

//     frameRateRange.Set(minFPS, maxFPS, expectedFPS);
// }

static AnimatorResult* GetAnimatorResult(ani_env *env, ani_object obj)
{
    ani_long animatorResultForAni;
    std::cout << "AniAnimation Object_GetFieldByName_Long 00" << std::endl;
    if(ANI_OK != env->Object_GetFieldByName_Long(obj, "animatorResult", &animatorResultForAni)){
        std::cout << "AniAnimation Object_GetFieldByName_Long 11" << std::endl;
        return nullptr;
    }
    std::cout << "AniAnimation Object_GetFieldByName_Long 22" << std::endl;
    return reinterpret_cast<AnimatorResult *>(animatorResultForAni);
}

//ani
static void JSReverse(ani_env *env, ani_object obj)
{
    std::cout << "AniAnimation JSReverse" << std::endl;
    auto animatorResult = GetAnimatorResult(env, obj);
    if (animatorResult == nullptr) {
        std::cout << "AniAnimation nullptr JSReverse" << std::endl;
        return;
    }
    // GetMotion
    if (animatorResult->GetMotion()) {
        std::cout << "AniAnimation nullptr GetMotion return" << std::endl;
        return;
    }
    auto animator = animatorResult->GetAnimator();
    if (!animator) {
        std::cout << "AniAnimation nullptr GetAnimator return" << std::endl;
        return;
    }
    if (!animator->HasScheduler()) {
        // AttachSchedulerOnContainer
        auto result = animator->AttachSchedulerOnContainer();
        if (!result) {
            std::cout << "AniAnimation HasScheduler return" << std::endl;
            return;
        }
    }
    animator->Reverse();
}

//ani
static void JSCancel(ani_env *env, ani_object obj)
{
    std::cout << "AniAnimation JSCancel start" << std::endl;
    auto animatorResult = GetAnimatorResult(env, obj);
    if (animatorResult == nullptr) {
        return;
    }
    auto animator = animatorResult->GetAnimator();
    if (animator == nullptr) {
        return;
    }
    animator->Cancel();
    std::cout << "AniAnimation JSCancel end" << std::endl;
}

//ani
static void JSPause(ani_env *env, ani_object obj)
{
    std::cout << "AniAnimation JSPause start" << std::endl;
    auto animatorResult = GetAnimatorResult(env, obj);
    if (animatorResult == nullptr) {
        return;
    }
    auto animator = animatorResult->GetAnimator();
    if (animator == nullptr) {
        return;
    }
    animator->Pause();
    std::cout << "AniAnimation JSPause end" << std::endl;
}

//ani
static void JSFinish(ani_env *env, ani_object obj)
{
    std::cout << "AniAnimation JSFinish start" << std::endl;
    auto animatorResult = GetAnimatorResult(env, obj);
    if (animatorResult == nullptr) {
        return;
    }
    auto animator = animatorResult->GetAnimator();
    if (animator == nullptr) {
        return;
    }
    animator->Finish();
    std::cout << "AniAnimation JSFinish end" << std::endl;
}

//ani
static void JSPlay(ani_env *env, ani_object obj)
{
    auto animatorResult = GetAnimatorResult(env, obj);
    if (animatorResult == nullptr) {

        return;
    }
    auto animator = animatorResult->GetAnimator();
    if (!animator) {
        std::cout << "AniAnimation nullptr return 2222" << std::endl;
        return;
    }
    if (!animator->HasScheduler()) {
        auto result = animator->AttachSchedulerOnContainer();
        if (!result) {
            std::cout << "AniAnimation nullptr HasScheduler 2222" << std::endl;
            return;
        }
    }
    if (animatorResult->GetMotion()) {
        animator->PlayMotion(animatorResult->GetMotion());
    } else {
        animator->Play();
    }
    animator->PrintVsyncInfoIfNeed();
    std::cout << "animator JSPlay end" << std::endl;
}

//ani
// static void JSSetExpectedFrameRateRange(ani_env *env, ani_object obj, ani_object frameRateRangeObject)
// {
//     auto animatorResult = GetAnimatorResult(env, obj);
//     if (animatorResult == nullptr) {
//         return;
//     }
//     auto animator = animatorResult->GetAnimator();
//     if (!animator) {
//         return;
//     }
//     if (!animator->HasScheduler()) {
//         auto result = animator->AttachSchedulerOnContainer();
//         if (!result) {
//             return;
//         }
//     }
//     FrameRateRange frameRateRange;

//     // //解析
//     // ani_int minFPS = 0;
//     // if (ANI_OK != env_->Object_GetFieldByName_Int(frameRateRangeObject, "min", &minFPS)) {
//     //     return;
//     // }
//     // ani_int maxFPS = 0;
//     // if (ANI_OK != env_->Object_GetFieldByName_Int(frameRateRangeObject, "max", &maxFPS)) {
//     //     return;
//     // }
//     // ani_int expectedFPS = 0;
//     // if (ANI_OK != env_->Object_GetFieldByName_Int(frameRateRangeObject, "expected", &expectedFPS)) {
//     //     return;
//     // }
//     // frameRateRange.Set(minFPS, maxFPS, expectedFPS);
//     // if (!frameRateRange.IsValid()) {
//     //     return nullptr;
//     // }

//     ParseExpectedFrameRateRange(env, frameRateRangeObject, frameRateRange);
//     animator->SetExpectedFrameRateRange(frameRateRange);
// }


static void ParseAnimatorOption(ani_env *env, ani_object obj, std::shared_ptr<AnimatorOption> option)
{
    std::cout << "AniAnimation ParseAnimatorOption start" << std::endl;
    ani_int durationAni;
    ani_int iterationsAni;
    ani_int delayAni;
    ani_ref easingAni;
    ani_ref fillAni;
    ani_ref directionAni;
    ani_double beginAni;
    ani_double endAni;

    env->Object_GetPropertyByName_Int(obj, "duration", &durationAni);
    env->Object_GetPropertyByName_Int(obj, "iterations", &iterationsAni);
    env->Object_GetPropertyByName_Int(obj, "delay", &delayAni);

    env->Object_GetPropertyByName_Double(obj, "begin", &beginAni);
    env->Object_GetPropertyByName_Double(obj, "end", &endAni);

    env->Object_GetPropertyByName_Ref(obj, "easing", &easingAni);
    env->Object_GetPropertyByName_Ref(obj, "fill", &fillAni);
    env->Object_GetPropertyByName_Ref(obj, "direction", &directionAni);
    std::cout << "AniAnimation ParseAnimatorOption mid" << std::endl;
    std::string easing = "ease";
    std::string fill = "none";
    std::string direction = "normal";
    easing = ANIUtils_ANIStringToStdString(env, reinterpret_cast<ani_string>(easingAni));
    fill = ANIUtils_ANIStringToStdString(env, reinterpret_cast<ani_string>(fillAni));
    direction = ANIUtils_ANIStringToStdString(env, reinterpret_cast<ani_string>(directionAni));
    
    option->duration = std::max(durationAni, 0);
    // if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
    //     option->delay = delayAni;
    // } else {
    //     option->delay = std::max(delayAni, 0);
    // }
    option->iterations = iterationsAni >= -1 ? iterationsAni : 1;
    option->begin = beginAni;
    option->end = endAni;
    option->easing = easing;
    option->fill = fill;
    option->direction = direction;
    std::cout << "AniAnimation ParseAnimatorOption end" << std::endl;
}

ani_object ANICreate(ani_env *env, [[maybe_unused]] ani_object object, [[maybe_unused]] ani_object aniOption)
{
    std::cout << "AniAnimation: ANICreate begin" << std::endl;
    ani_object animator_obj = {};

    static const char *className = "Ljs_animator/AnimatorResult;";
    ani_class cls;
    if (ANI_OK != env->FindClass(className, &cls)) {
        std::cerr << "AniAnimation: Not found '" << className << "'" << std::endl;
        return animator_obj;
    }

    //查找构造函数，如果没有重载，可以直接传递 nullptr
    ani_method ctor;
    if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", nullptr, &ctor)){
        std::cerr << "AniAnimation: get ctor Failed'" << className << "'" << std::endl;
        return animator_obj;
    }

    //  解析option、构造一个animator并创建一个 animatorResult 的实例
    auto option = std::make_shared<AnimatorOption>();
    ParseAnimatorOption(env, aniOption, option);
    std::cout << option->ToString() << std::endl;
    auto animator = CREATE_ANIMATOR("ohos.animator");
    animator->AttachSchedulerOnContainer();
    auto animatorResult = new AnimatorResult(animator, option);

    // 将cpp的animatorResult对象和前端对象绑定
    if (ANI_OK != env->Object_New(cls, ctor, &animator_obj, reinterpret_cast<ani_long>(animatorResult))) {
        std::cerr << "AniAnimation:Create Object Failed'" << className << "'" << std::endl;
        return animator_obj;
    }

    std::cout << "AniAnimation:ANICreate finish no err" << std::endl;
    return animator_obj;
}

void ANIUpdate(ani_env *env, [[maybe_unused]] ani_object object, [[maybe_unused]] ani_object options)
{
    std::cout << "AniAnimation: ANIUpdate begin" << std::endl;
}

ani_status BindAnimator(ani_env *env)
{
    static const char *className = "Ljs_animator/Animator;";
    ani_class cls;
    if (ANI_OK != env->FindClass(className, &cls)) {
        std::cerr << "AniAnimation:Not found '" << className << "'" << std::endl;
        return ANI_ERROR;
    }

    std::array methods = {
        ani_native_function {"create", "Ljs_animator/AnimatorOptions;:Ljs_animator/AnimatorResult;", reinterpret_cast<void *>(ANICreate)},
    };

    if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) {
        std::cerr << "AniAnimation:Cannot bind native methods to '" << className << "'" << std::endl;
        return ANI_ERROR;
    };
    return ANI_OK;
}

ani_status BindAnimatorResult(ani_env *env)
{
    static const char *className = "Ljs_animator/AnimatorResult;";
    ani_class cls;
    if (ANI_OK != env->FindClass(className, &cls)) {
        std::cerr << "AniAnimation:Not found '" << className << "'" << std::endl;
        return ANI_ERROR;
    }

    std::array methods = {
        ani_native_function {"update", "Ljs_animator/AnimatorOptions;:V", reinterpret_cast<void *>(ANIUpdate)},
        // ani_native_function {"<set> oncancel", "I:V", reinterpret_cast<void *>(ANISetOnCancel)},
        ani_native_function {"play", ":V", reinterpret_cast<void *>(JSPlay)},
        ani_native_function {"reverse", ":V", reinterpret_cast<void *>(JSReverse)},
        ani_native_function {"cancel", ":V", reinterpret_cast<void *>(JSCancel)},
        ani_native_function {"pause", ":V", reinterpret_cast<void *>(JSPause)},
        ani_native_function {"finish", ":V", reinterpret_cast<void *>(JSFinish)},
    };

    if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) {
        std::cerr << "AniAnimation:Cannot bind native methods to '" << className << "'" << std::endl;
        return ANI_ERROR;
    };
    return ANI_OK;
}

} // namespace

ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
{
    ani_env *env;
    std::cerr << "AniAnimation:ANI_Constructor enter" << std::endl;
    if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
        std::cerr << "AniAnimation:Unsupported ANI_VERSION_1" << std::endl;
        return ANI_ERROR;
    }

    // bind Animator class.
    ani_status retValue = ANI_OK ;
    retValue = OHOS::Ace::Ani::BindAnimator(env);
    if (retValue != ANI_OK) {
        return retValue;
    }
    retValue = OHOS::Ace::Ani::BindAnimatorResult(env);
    if (retValue != ANI_OK) {
        return retValue;
    }

    *result = ANI_VERSION_1;
    std::cerr << "AniAnimation:ANI_Constructor finish" << std::endl;
    return ANI_OK;
}